/*!
\copyright  Copyright (c) 2019-2025 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief      Microphone component implementation, responsible for configuration and microphone user tracking
*/

#include "microphones.h"
#include "microphones_platform_config.h"
#include <audio_plugin_common.h>
#include <logging.h>

#include <stdlib.h>

typedef struct
{
    audio_mic_params* config;
    unsigned non_exclusive_users;
    microphone_user_type_t microphone_user;
    uint32 current_sample_rate;
    int* roles;
    unsigned num_roles;
} managed_microphone;

static managed_microphone* microphones;
static unsigned num_microphones = 0;
static unsigned num_microphones_for_voice = PLATFORM_NUM_MICROPHONES_FOR_VOICE;

static microphones_bias_config_t* bias;
static unsigned num_bias = 0;

static bool microphones_IsValidMicrophoneNumber(uint16 microphone_number)
{
    return (microphone_number < num_microphones);
}

static void microphones_SetMicrophonesConfig(const microphones_mic_config_t mics_config[], unsigned mics_count)
{
    PanicNull((void*)mics_config);
    PanicZero(mics_count);
    
    if (microphones)
    {
        for (uint16 i = 0; i < num_microphones; i++)
        {
            free(microphones[i].config);
            free(microphones[i].roles);
        }
        free(microphones);
        microphones = NULL;
        num_microphones = 0;
    }

    num_microphones = mics_count;
    microphones = PanicUnlessMalloc(num_microphones * sizeof(*microphones));
    memset(microphones, 0, num_microphones * sizeof(*microphones));

    for(uint16 microphone_number = 0; microphone_number < num_microphones; microphone_number++)
    {
        managed_microphone *mic = &microphones[microphone_number];

        mic->config = PanicUnlessMalloc(sizeof(*mic->config));
        memcpy(mic->config, &mics_config[microphone_number].params, sizeof(*mic->config));

        mic->num_roles = mics_config[microphone_number].num_roles;
        if(mic->num_roles)
        {
            mic->roles = PanicUnlessMalloc(sizeof(*mic->roles) * mic->num_roles);
            memcpy(mic->roles, mics_config[microphone_number].roles, mic->num_roles * sizeof(*mic->roles));
        }

        mic->non_exclusive_users = 0;
        mic->microphone_user = invalid_user;
        mic->current_sample_rate = 0;
    }

    return;
}

static void microphones_SetBiasConfig(const microphones_bias_config_t *bias_config, unsigned bias_count)
{
    PanicNull((void*)bias_config);
    PanicZero(bias_count);
    
    if (bias)
    {
        free(bias);
        bias = NULL;
        num_bias = 0;
    }

    num_bias = bias_count;
    size_t bias_size = sizeof(*bias) * num_bias;
    bias = PanicUnlessMalloc(bias_size);
    memcpy(bias, bias_config, bias_size);

    return;
}

const audio_mic_params * Microphones_GetMicrophoneConfig(uint16 microphone_number)
{
    PanicFalse(microphones_IsValidMicrophoneNumber(microphone_number));
    return microphones[microphone_number].config;
}

void Microphones_SetMicrophoneConfig(uint16 microphone_number, audio_mic_params *config)
{
    PanicFalse(microphones_IsValidMicrophoneNumber(microphone_number));
    memcpy(microphones[microphone_number].config, config, sizeof(*microphones[microphone_number].config));
}

static microphone_user_type_t microphones_GetMicrophoneUserType(uint16 microphone_number)
{
    return (microphones[microphone_number].microphone_user);
}

static inline uint32 microphones_GetCurrentSampleRate(uint16 microphone_number)
{
    return (microphones[microphone_number].current_sample_rate);
}

static inline bool microphones_IsMicrophoneInExclusiveUse(uint16 microphone_number)
{
    return (microphones_GetMicrophoneUserType(microphone_number) == normal_priority_user ||
            microphones_GetMicrophoneUserType(microphone_number) == high_priority_user);
}

static inline bool microphones_IsMicrophoneInNonExclusiveUse(uint16 microphone_number)
{
    return !!(microphones[microphone_number].non_exclusive_users);
}

static inline void microphones_AddUser(uint16 microphone_number, microphone_user_type_t microphone_user_type)
{
    if(microphone_user_type == non_exclusive_user)
    {
        microphones[microphone_number].non_exclusive_users++;
         DEBUG_LOG("microphones_AddUser: microphone[%d] non_exclusive_users=%d", microphone_number,
                   microphones[microphone_number].non_exclusive_users);
    }
    else
    {
        microphones[microphone_number].microphone_user = microphone_user_type;
    }
}

static inline void microphones_RemoveUser(uint16 microphone_number, microphone_user_type_t microphone_user_type)
{
    if(microphone_user_type == non_exclusive_user)
    {
        microphones[microphone_number].non_exclusive_users--;
        DEBUG_LOG("microphones_RemoveUser: microphone[%d] non_exclusive_users=%d", microphone_number,
                  microphones[microphone_number].non_exclusive_users);
    }
    else
    {
        microphones[microphone_number].microphone_user = invalid_user;
    }
}

static inline void microphones_SetCurrentSampleRate(uint16 microphone_number, uint32 sample_rate)
{
    microphones[microphone_number].current_sample_rate = sample_rate;
}

static unsigned microphones_GetMicrophoneBiasVoltage(mic_bias_id id)
{
    unsigned bias_voltage = 0;
    if (id == MIC_BIAS_0)
    {
        if (bias[0].id == BIAS_CONFIG_MIC_BIAS_0)
            bias_voltage = bias[0].voltage;
        else if (bias[1].id == BIAS_CONFIG_MIC_BIAS_0)
            bias_voltage = bias[1].voltage;
        else
            Panic();
    }
    else if (id == MIC_BIAS_1)
    {
        if (bias[0].id == BIAS_CONFIG_MIC_BIAS_1)
            bias_voltage = bias[0].voltage;
        else if (bias[1].id == BIAS_CONFIG_MIC_BIAS_1)
            bias_voltage = bias[1].voltage;
        else
            Panic();
    }
    else
        Panic();

    DEBUG_LOG("microphones_GetMicrophoneBiasVoltage, id %u, bias %u", id, bias_voltage);
    return bias_voltage;
}

void Microphones_SetMicRate(uint16 microphone_number, uint32 sample_rate, microphone_user_type_t microphone_user_type)
{
    PanicFalse(microphones_IsValidMicrophoneNumber(microphone_number));
    PanicFalse(microphone_user_type != invalid_user);

    if(((microphone_user_type > normal_priority_user) && (microphones_GetMicrophoneUserType(microphone_number) != microphone_user_type))
            || (microphones_IsMicrophoneInExclusiveUse(microphone_number) == FALSE))
    {
        const audio_mic_params * microphone_config = Microphones_GetMicrophoneConfig(microphone_number);
        uint32 current_sample_rate = microphones_GetCurrentSampleRate(microphone_number);
        if(current_sample_rate != sample_rate)
        {
            DEBUG_LOG("Microphones_SetMicRate: microphone_number %d, rate=%lu, enum:mic_user_type_t:%d",
                      microphone_number, sample_rate, microphone_user_type);
            AudioPluginSetMicRate(microphone_config->channel, *microphone_config, sample_rate);
            microphones_SetCurrentSampleRate(microphone_number, sample_rate);
        }
    }
}

Source Microphones_TurnOnMicrophone(uint16 microphone_number, microphone_user_type_t microphone_user_type)
{
    Source mic_source = NULL;
    PanicFalse(microphones_IsValidMicrophoneNumber(microphone_number));
    PanicFalse(microphone_user_type != invalid_user);

    if(((microphone_user_type > normal_priority_user) && (microphones_GetMicrophoneUserType(microphone_number) != microphone_user_type))
            || (microphones_IsMicrophoneInExclusiveUse(microphone_number) == FALSE))
    {
        const audio_mic_params * microphone_config = Microphones_GetMicrophoneConfig(microphone_number);
        bool already_in_use = Microphones_IsMicrophoneInUse(microphone_number);
        if(!already_in_use)
        {
            DEBUG_LOG("Microphones_TurnOnMicrophone: microphone_number %d enum:mic_type_t:%d audio HDW instance %d",
                      microphone_number, microphone_config->type, microphone_config->instance);
            AudioPluginSetMicGain(microphone_config->channel, *microphone_config);
            mic_source =  AudioPluginMicSetup(microphone_config->channel, *microphone_config);
        }
        else
        {
            mic_source = AudioPluginGetMicSource(*microphone_config, microphone_config->channel);
        }
        microphones_AddUser(microphone_number, microphone_user_type);
    }
    DEBUG_LOG("Microphones_TurnOnMicrophone: source=%p", mic_source);
    return mic_source;
}

void Microphones_TurnOffMicrophone(uint16 microphone_number, microphone_user_type_t microphone_user_type)
{
    DEBUG_LOG("Microphones_TurnOffMicrophone: number=%d, user_type=%d", microphone_number, microphone_user_type);
    PanicFalse(microphones_IsValidMicrophoneNumber(microphone_number));
    PanicFalse(microphone_user_type != invalid_user);

    if(Microphones_IsMicrophoneInUse(microphone_number))
    {
        bool close_mic;
        microphones_RemoveUser(microphone_number, microphone_user_type);
        close_mic = (Microphones_IsMicrophoneInUse(microphone_number) == FALSE);
        DEBUG_LOG("Microphones_TurnOffMicrophone: shutting down, close_mic=%u", close_mic);
        if(close_mic)
        {
            const audio_mic_params * microphone_config = Microphones_GetMicrophoneConfig(microphone_number);
            AudioPluginMicShutdown(microphone_config->channel, microphone_config, close_mic);
            microphones_SetCurrentSampleRate(microphone_number, 0);
        }
    }
}

uint8 Microphones_MaxSupported(void)
{
    return num_microphones;
}

void Microphones_Init(const microphones_config_t* microphones_config)
{
    const microphones_mic_config_t* mics_config = NULL;
    uint16 num_mics = 0;

    if (microphones_config && microphones_config->mics_config)
    {
        mics_config = microphones_config->mics_config;
        num_mics = microphones_config->num_mics;
    }
    else
    {
        mics_config = platform_microphone_configs;
        num_mics = platform_num_mics;
    }

    microphones_SetMicrophonesConfig(mics_config, num_mics);

    const microphones_bias_config_t* bias_config = NULL;

    if (microphones_config && microphones_config->bias_config)
    {
        bias_config = microphones_config->bias_config;
        num_bias = microphones_config->num_bias;
    }
    else
    {
        bias_config = platform_mic_bias_config;
        num_bias = platform_num_bias;
    }

    microphones_SetBiasConfig(bias_config, num_bias);

    AudioPluginCommonRegisterMicBiasVoltageCallback(microphones_GetMicrophoneBiasVoltage);
}

Source Microphones_GetMicrophoneSource(uint16 microphone_number)
{
    Source mic_source = NULL;
    DEBUG_LOG("Microphones_GetMicrophoneSource: number=%d", microphone_number);
    PanicFalse(microphones_IsValidMicrophoneNumber(microphone_number));

    if (Microphones_IsMicrophoneInUse(microphone_number))
    {
        const audio_mic_params * microphone_config = Microphones_GetMicrophoneConfig(microphone_number);
        mic_source = AudioPluginGetMicSource(*microphone_config, microphone_config->channel);
    }

    return mic_source;
}

bool Microphones_IsMicrophoneInUse(uint16 microphone_number)
{
    return (microphones_IsMicrophoneInNonExclusiveUse(microphone_number) || microphones_IsMicrophoneInExclusiveUse(microphone_number));
}

uint16 Microphones_GetMicrophoneByRole(int mic_role)
{
    for (unsigned i = 0; i < num_microphones; i++)
    {
        managed_microphone *mic = &microphones[i];
        for (unsigned n = 0; n < mic->num_roles; n++)
        {
            if (mic->roles[n] == mic_role)
            {
                return i;
            }
        }
    }

    return MICROPHONE_NONE;
}

void Microphones_SetNumberOfMicrophonesForVoice(uint16 num_mics_for_voice)
{
    PanicFalse(num_mics_for_voice <= num_microphones);
    num_microphones_for_voice = num_mics_for_voice;
}

uint16 Microphones_GetNumberOfMicrophonesForVoice(void)
{
    return num_microphones_for_voice;
}
